package com.zhangkunhao.notes.util;


import com.zhangkunhao.notes.bean.PooledConnection;

import java.io.FileInputStream;
import java.io.IOException;
import java.sql.*;
import java.util.Properties;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 自动扩容连接池的实现逻辑与参数配置
 *
 * @author 20642
 */
public class MyPoolImpl implements MyPool {
    public MyPoolImpl() {
        init();
    }

    private static String driver = null;
    private static String url = null;
    private static String user = null;
    private static String password = null;
    /**
     * 连接池中管道参数
     **/
    private static int initCount;
    /**
     * 每次扩容新增的连接数
     */
    private static int stepSize;
    private static int poolMaxSize;
    /**
     * 扩容次数
     */
    private static int expandTime;

    /**
     * 线程安全集合,用来放(可复用)数据库连接管道(集合之前用Vector,但在测试的时候发现多线程并发出错了ConcurrentModificationException)？
     **/
    public static CopyOnWriteArrayList<PooledConnection> pooledConnections = new CopyOnWriteArrayList<>();

    /**
     * 注册驱动
     */
    private void init() {

        //创建配置文件对象
        Properties p = new Properties();
        try {
            p.load(new FileInputStream("src/jdbc.properties"));
        } catch (IOException e) {
            //若这里抛出异常则下面不运行
            e.printStackTrace();
        }
        driver = p.getProperty("driverClassName");
        url = p.getProperty("url");
        user = p.getProperty("user");
        password = p.getProperty("password");
        expandTime = Integer.valueOf(p.getProperty("expandTime"));
        if (Integer.valueOf(p.getProperty("initCount")) > 0) {
            initCount = Integer.valueOf(p.getProperty("initCount"));
        }
        if (Integer.valueOf(p.getProperty("stepSize")) > 0) {
            stepSize = Integer.valueOf(p.getProperty("stepSize"));
        }
        if (Integer.valueOf(p.getProperty("poolMaxSize")) > 0) {
            poolMaxSize = Integer.valueOf(p.getProperty("poolMaxSize"));
        }
        System.out.println("最大连接池数量为:" + poolMaxSize);
        //准备创建DriverManager
        try {
            //注册驱动
            Class.forName(driver);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        //获取连接,用create方法获取
        /**DriverManager.getConnection(url, user, password);**/
        this.createConnections(initCount);
    }


    @Override
    public synchronized PooledConnection getPooledConnection() {
//          public PooledConnection getPooledConnection()

        //要拿连接,先判断管道集合中是否有连接
        if (pooledConnections.size() == 0) {
            System.out.println("连接池没有连接！");
            //如果没有就手动再建一把连接池
            this.createConnections(initCount);
        }
        PooledConnection connection = getRealConnection();
        //如果还是没有拿到,说明全部线程都处于busy状态,得扩容
        while (connection == null) {
            this.createConnections(stepSize);
            connection = getRealConnection();
            try {
                //拿到连接等待一会,防止连接又被别的线程抢夺
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return connection;
    }

    @Override
    public void createConnections(int count) {
        expandTime++;
        System.out.println("第" + expandTime + "次扩容,扩容量为:" + count);
        if ((pooledConnections.size() + count) <= poolMaxSize) {
            for (int i = 0; i < count; i++) {
                try {
                    //获取连接放入线程安全的连接池中
                    Connection conn = DriverManager.getConnection(url, user, password);
                    PooledConnection pooledConnection = new PooledConnection(false, conn, String.valueOf(i));
                    pooledConnections.add(pooledConnection);
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        } else {
            throw new RuntimeException("连接池已经达到最大扩容的极限，请在配置文件重新设置最大连接数");
        }
        System.out.println("当前连接池的连接数量:" + pooledConnections.size());

    }

    /*
     *功能描述: 同步方法,真正的获取连接(连接包装类内包括:connection和标志位busy)
     * @param null
     * @author 张坤浩
     * @date 2023/4/1 21:12
     * @return
     */

    private PooledConnection getRealConnection() {
        //遍历连接池的所有连接
        for (PooledConnection conn : pooledConnections) {
            //判断该连接是否已被占用
            if (!conn.isBusy()) {
                //false为可用(空闲),true为占用(繁忙)
                Connection connection = conn.getConnection();
                try {
                    //判断该连接是否在设定时间连接通数据库(连接通为true)
                    if (!connection.isValid(2000)) {
                        connection = DriverManager.getConnection(url, user, password);
                    }
                } catch (SQLException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                conn.setBusy(true);
                return conn;
            }
        }
        //如果连接池中的连接都被占用,则返回null,由调用函数处理(即扩容)
        return null;
    }

    /**
     * 关闭资源，注意关闭从下往上关
     *
     * @param conn
     * @param ps
     */
    public static synchronized void free(PooledConnection conn, PreparedStatement ps, ResultSet rs) {
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (ps != null) {
            try {
                ps.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        //回收连接,将该连接置为可用,而不是真正关掉连接
        if (conn != null) {
            conn.setBusy(false);
        }
    }
}
